; Attempts to lock the lcd for use
; outputs	a  = status code
RequestLock:
	call LCDReady
	if(LCD_LOCKED)
		ret z		; if the lcd is already locked then return with the status code
	ld a,(CurrentThreadID)	; otherwise get the current thread ID
	ld (LCDLock),a		; and lock the lcd to it
	return(LCD_LOCK_GRANTED); and return the status code
; Returns the status of the lcd for the thread
; outputs	a  = status code
LCDReady:
	ld a,(LCDLock)
	cp -1
		jr z,_
	ld b,a
	ld a,(CurrentThreadID)
	cp b
		jr z,_
	return(LCD_LOCKED)
_	return(LCD_READY)
WaitForLCD:
	call LCDReady
	if(LCD_LOCKED)
		jr z,WaitForLCD
	ret
; Do nothing for a few cycles
LCDDelay:
	push hl
	push de
	pop de
	pop hl
	nop
	ret
; Copies the backbuffer to the screen
FastCopy:
	push af
	call SafeInterruptDI
	push af
	push bc
	push de
	push hl
	push ix
	ld a,(PanelEnabled)
	if(DISABLED)
		call nz,DrawPanel
	ld hl,LCDBuffer  ;This can be commented out or another entry placed after it
                           ;so the buffer can be provided by option.
	ld c,010h
	ld a,080h
setrow:
	in f,(c)
	jp m,setrow
	out (10h),a
	ld de,12
	ld a,20h
col:
	in f,(c)
	jp m,col
	out (10h),a
	push af
	ld b,64
row:
	ld a,(hl)
rowwait:
	in f,(c)
	jp m,rowwait
	out (11h),a
	add hl,de
	djnz row
	pop af
	dec h
	dec h
	dec h
	inc hl
	inc a
	cp 2ch
	jp nz,col
	pop ix
	pop hl
	pop de
	pop bc
	pop af
	jr c,$+3
	ei
	pop af
	ret
ClearBuffer:
	xor	a
FillBuffer:
	push de
	push bc
		ld d,a
		ld e,a
		call SafeInterruptDI
		push af
		ld (saveSP),sp
		ld sp,LCDBuffer+768
		ld b,32
_			push de
			push de
			push de
			push de
			push de
			push de
			push de
			push de
			push de
			push de
			push de
			push de
			djnz -_
		ld sp,(saveSP)
	pop af
	pop bc
	pop de
		ret c
	ei
	ret
PutSpriteXOR:
; D = xpos
; E = ypos
; B = height
; IX = image address
; Start by doing vertical clipping
    LD     A, 11111111b         ; Reset clipping mask
    LD     (clip_mask), A
    LD     A, E                 ; If ypos is negative
    OR     A                    ; try clipping the top
    JP     M, ClipTop           ;
 
    SUB    64                   ; If ypos is >= 64
    RET    NC                   ; sprite is off-screen

    NEG                         ; If (64 - ypos) > height
    CP     B                    ; don't need to clip
    JR     NC, VertClipDone     ; 

    LD     B, A                 ; Do bottom clipping by
    JR     VertClipDone         ; setting height to (64 - ypos)

ClipTop:
    LD     A, B                 ; If ypos <= -height
    NEG                         ; sprite is off-screen
    SUB    E                    ;
    RET    NC                   ;

    PUSH   AF
    ADD    A, B                 ; Get the number of clipped rows
    LD     E, 0                 ; Set ypos to 0 (top of screen)
    LD     B, E                 ; Advance image data pointer
    LD     C, A                 ;
    ADD    IX, BC               ;
    POP    AF
    NEG                         ; Get the number of visible rows
    LD     B, A                 ; and set as height

VertClipDone:
; Now we're doing horizontal clipping
    LD     C, 0                 ; Reset correction factor
    LD     A, D

    CP     -7                   ; If 0 > xpos >= -7
    JR     NC, ClipLeft         ; clip the left side

    CP     96                   ; If xpos >= 96
    RET    NC                   ; sprite is off-screen

    CP     89                   ; If 0 <= xpos < 89
    JR     C, HorizClipDone     ; don't need to clip

ClipRight:
    AND    7                    ; Determine the clipping mask
    LD     C, A
    LD     A, 11111111b
FindRightMask:
    ADD    A, A
    DEC    C
    JR     NZ, FindRightMask
    LD     (clip_mask), A
    LD     A, D
    JR     HorizClipDone

ClipLeft:
    AND    7                    ; Determine the clipping mask
    LD     C, A
    LD     A, 11111111b
FindLeftMask:
    ADD    A, A
    DEC    C
    JR     NZ, FindLeftMask
    CPL
    LD     (clip_mask), A
    LD     A, D
    ADD    A, 96                ; Set xpos so sprite will "spill over"
    LD     C, 12                ; Set correction

HorizClipDone:
; A = xpos
; E = ypos
; B = height
; IX = image address

; Now we can finally display the sprite.
    LD     H, 0
    LD     D, H
    LD     L, E
    ADD    HL, HL
    ADD    HL, DE
    ADD    HL, HL
    ADD    HL, HL

    LD     E, A
    SRL    E
    SRL    E
    SRL    E
    ADD    HL, DE

    LD     DE, LCDBuffer
    ADD    HL, DE

    LD     D, 0                 ; Correct graph buffer address
    LD     E, C                 ; if clipping the left side
    SBC    HL, DE               ;

    AND    7
    JR     Z, _Aligned

    LD     C, A
    LD     DE, 11

_RowLoop:
    PUSH   BC
    LD     B, C
    LD     A, (clip_mask)       ; Mask out the part of the sprite
    AND    (IX)                 ; to be horizontally clipped
    LD     C, 0

_ShiftLoop:
    SRL    A
    RR     C
    DJNZ   _ShiftLoop

    XOR    (HL)
    LD     (HL), A

    INC    HL
    LD     A, C
    XOR    (HL)
    LD     (HL), A

    ADD    HL, DE
    INC    IX
    POP    BC
    DJNZ   _RowLoop
    RET

_Aligned:
    LD     DE, 12

_PutLoop:
    LD     A, (IX)
    XOR    (HL)
    LD     (HL), A
    INC    IX
    ADD    HL, DE
    DJNZ   _PutLoop
    RET

PutSpriteOR:
; D = xpos
; E = ypos
; B = height
; IX = image address
; Start by doing vertical clipping
    LD     A, 11111111b         ; Reset clipping mask
    LD     (clip_mask), A
    LD     A, E                 ; If ypos is negative
    OR     A                    ; try clipping the top
    JP     M, ClipTopOR           ;
 
    SUB    64                   ; If ypos is >= 64
    RET    NC                   ; sprite is off-screen

    NEG                         ; If (64 - ypos) > height
    CP     B                    ; don't need to clip
    JP     NC, VertClipDoneOR     ; 

    LD     B, A                 ; Do bottom clipping by
    JP     VertClipDoneOR         ; setting height to (64 - ypos)

ClipTopOR:
    LD     A, B                 ; If ypos <= -height
    NEG                         ; sprite is off-screen
    SUB    E                    ;
    RET    NC                   ;

    PUSH   AF
    ADD    A, B                 ; Get the number of clipped rows
    LD     E, 0                 ; Set ypos to 0 (top of screen)
    LD     B, E                 ; Advance image data pointer
    LD     C, A                 ;
    ADD    IX, BC               ;
    POP    AF
    NEG                         ; Get the number of visible rows
    LD     B, A                 ; and set as height

VertClipDoneOR:
; Now we're doing horizontal clipping
    LD     C, 0                 ; Reset correction factor
    LD     A, D

    CP     -7                   ; If 0 > xpos >= -7
    JR     NC, ClipLeftOR       ; clip the left side

    CP     96                   ; If xpos >= 96
    RET    NC                   ; sprite is off-screen

    CP     89                   ; If 0 <= xpos < 89
    JR     C, HorizClipDoneOR   ; don't need to clip

ClipRightOR:
    AND    7                    ; Determine the clipping mask
    LD     C, A
    LD     A, 11111111b
FindRightMaskOR:
    ADD    A, A
    DEC    C
    JR     NZ, FindRightMaskOR
    LD     (clip_mask), A
    LD     A, D
    JR     HorizClipDoneOR

ClipLeftOR:
    AND    7                    ; Determine the clipping mask
    LD     C, A
    LD     A, 11111111b
FindLeftMaskOR:
    ADD    A, A
    DEC    C
    JR     NZ, FindLeftMaskOR
    CPL
    LD     (clip_mask), A
    LD     A, D
    ADD    A, 96                ; Set xpos so sprite will "spill over"
    LD     C, 12                ; Set correction

HorizClipDoneOR:
; A = xpos
; E = ypos
; B = height
; IX = image address

; Now we can finally display the sprite.
    LD     H, 0
    LD     D, H
    LD     L, E
    ADD    HL, HL
    ADD    HL, DE
    ADD    HL, HL
    ADD    HL, HL

    LD     E, A
    SRL    E
    SRL    E
    SRL    E
    ADD    HL, DE

    LD     DE, LCDBuffer
    ADD    HL, DE

    LD     D, 0                 ; Correct graph buffer address
    LD     E, C                 ; if clipping the left side
    SBC    HL, DE               ;

    AND    7
    JR     Z, _AlignedOR

    LD     C, A
    LD     DE, 11

_RowLoopOR:
    PUSH   BC
    LD     B, C
    LD     A, (clip_mask)       ; Mask out the part of the sprite
    AND    (IX)                 ; to be horizontally clipped
    LD     C, 0

_ShiftLoopOR:
    SRL    A
    RR     C
    DJNZ   _ShiftLoopOR

    or    (HL)
    LD     (HL), A

    INC    HL
    LD     A, C
    or    (HL)
    LD     (HL), A

    ADD    HL, DE
    INC    IX
    POP    BC
    DJNZ   _RowLoopOR
    RET

_AlignedOR:
    LD     DE, 12

_PutLoopOR:
    LD     A, (IX)
    XOR    (HL)
    LD     (HL), A
    INC    IX
    ADD    HL, DE
    DJNZ   _PutLoopOR
    RET
    
PutSpriteAND:
; D = xpos
; E = ypos
; B = height
; IX = image address
; Start by doing vertical clipping
    LD     A, 11111111b         ; Reset clipping mask
    LD     (clip_mask), A
    LD     A, E                 ; If ypos is negative
    OR     A                    ; try clipping the top
    JP     M, ClipTopAND           ;
 
    SUB    64                   ; If ypos is >= 64
    RET    NC                   ; sprite is off-screen

    NEG                         ; If (64 - ypos) > height
    CP     B                    ; don't need to clip
    JP     NC, VertClipDoneAND     ; 

    LD     B, A                 ; Do bottom clipping by
    JP     VertClipDoneAND         ; setting height to (64 - ypos)

ClipTopAND:
    LD     A, B                 ; If ypos <= -height
    NEG                         ; sprite is off-screen
    SUB    E                    ;
    RET    NC                   ;

    PUSH   AF
    ADD    A, B                 ; Get the number of clipped rows
    LD     E, 0                 ; Set ypos to 0 (top of screen)
    LD     B, E                 ; Advance image data pointer
    LD     C, A                 ;
    ADD    IX, BC               ;
    POP    AF
    NEG                         ; Get the number of visible rows
    LD     B, A                 ; and set as height

VertClipDoneAND:
; Now we're doing horizontal clipping
    LD     C, 0                 ; Reset correction factor
    LD     A, D

    CP     -7                   ; If 0 > xpos >= -7
    JR     NC, ClipLeftAND       ; clip the left side

    CP     96                   ; If xpos >= 96
    RET    NC                   ; sprite is off-screen

    CP     89                   ; If 0 <= xpos < 89
    JR     C, HorizClipDoneAND   ; don't need to clip

ClipRightAND:
    AND    7                    ; Determine the clipping mask
    LD     C, A
    LD     A, 11111111b
FindRightMaskAND:
    ADD    A, A
    DEC    C
    JR     NZ, FindRightMaskAND
    LD     (clip_mask), A
    LD     A, D
    JR     HorizClipDoneAND

ClipLeftAND:
    AND    7                    ; Determine the clipping mask
    LD     C, A
    LD     A, 11111111b
FindLeftMaskAND:
    ADD    A, A
    DEC    C
    JR     NZ, FindLeftMaskAND
    CPL
    LD     (clip_mask), A
    LD     A, D
    ADD    A, 96                ; Set xpos so sprite will "spill over"
    LD     C, 12                ; Set correction

HorizClipDoneAND:
; A = xpos
; E = ypos
; B = height
; IX = image address

; Now we can finally display the sprite.
    LD     H, 0
    LD     D, H
    LD     L, E
    ADD    HL, HL
    ADD    HL, DE
    ADD    HL, HL
    ADD    HL, HL

    LD     E, A
    SRL    E
    SRL    E
    SRL    E
    ADD    HL, DE

    LD     DE, LCDBuffer
    ADD    HL, DE

    LD     D, 0                 ; Correct graph buffer address
    LD     E, C                 ; if clipping the left side
    SBC    HL, DE               ;

    AND    7
    JR     Z, _AlignedAND

    LD     C, A
    LD     DE, 11

_RowLoopAND:
    PUSH   BC
    LD     B, C
    LD     A, (clip_mask)       ; Mask out the part of the sprite
    AND    (IX)                 ; to be horizontally clipped
    LD     C, 0

_ShiftLoopAND:
    SRL    A
    RR     C
    DJNZ   _ShiftLoopAND

    or    (HL)
    LD     (HL), A

    INC    HL
    LD     A, C
    or    (HL)
    LD     (HL), A

    ADD    HL, DE
    INC    IX
    POP    BC
    DJNZ   _RowLoopAND
    RET

_AlignedAND:
    LD     DE, 12

_PutLoopAND:
    LD     A, (IX)
    XOR    (HL)
    LD     (HL), A
    INC    IX
    ADD    HL, DE
    DJNZ   _PutLoopAND
    RET
    
LCDUp:	;shifts the LCD up by the amount in a
	push bc	;
	push de	;Preserve registers
	push hl	;
	ld b,a
_		push bc
		ld hl,LCDBuffer+12
		ld de,LCDBuffer
		ld bc,756
		ldir
		pop bc
		djnz -_
	pop hl	;
	pop de	;Restore registers
	pop bc	;
	ret
LCDDown:		;shifts the screen down the amount of lines in a
	push bc	;
	push de	;Preserve registers
	push hl	;
	ld b,a
_		push bc
		ld hl,LCDBuffer+767-12
		ld de,LCDBuffer+767
		ld bc,756
		lddr
		pop bc
		djnz -_
	pop hl	;
	pop de	;Restore registers
	pop bc	;
	ret
LCDRight:		;shifts the screen right the amount of lines in a
	push bc	;
	push de	;Preserve registers
	push hl	;
	ld b,a
LCDRightLoop:
		push bc
		ld hl,LCDBuffer
		ld c,64
LCDRightMainLoop:
		ld b,12
		or a
_			rr (hl)
			inc hl
			djnz -_
		dec c
		jr nz,LCDRightMainLoop
		pop bc
		djnz LCDRightLoop
	pop hl	;
	pop de	;Restore registers
	pop bc	;
	ret
LCDLeft:		;shifts the screen left the amount of lines in a
	push bc	;
	push de	;Preserve registers
	push hl	;
	ld b,a
LCDLeftLoop:
		push bc
		ld hl,LCDBuffer+767
		ld c,64
LCDLeftMainLoop:
		ld b,12
		or a
_			rl (hl)
			dec hl
			djnz -_
		dec c
		jr nz,LCDLeftMainLoop
		pop bc
		djnz LCDLeftLoop
	pop hl	;
	pop de	;Restore registers
	pop bc	;
	ret
;-----> Draw a picture
;Input:	ix->sprite
;	a=x
;	l=y
;	b=height	(in pixels)
;	c=width		(in bytes, e.g. 2 would be 16 pixels)
;Output: nothing
; All registers are destroyed except bc', de', hl'
LargeSpriteXOR:
	ex	af,af'
	ld	a,c
	push	af
	ex	af,af'
	ld	e,l
	ld	h,$00
	ld	d,h
	add	hl,de
	add	hl,de
	add	hl,hl
	add	hl,hl
	ld	e,a
	and	$07
	ld	c,a
	srl	e
	srl	e
	srl	e
	add	hl,de
	ld	de,LCDBuffer
	add	hl,de
_	push	hl	;largeSpriteLoop1
_	ld	d,(ix)	;largeSpriteLoop2
	ld	e,$00
	ld	a,c
	or	a
	jr	z,++_
_	srl	d	;largeSpriteLoop3
	rr	e
	dec	a
	jr	nz,-_
_	ld	a,(hl)	;largeSpriteSkip1
	xor	d
	ld	(hl),a
	inc	hl
	ld	a,(hl)
	xor	e
	ld	(hl),a
	inc	ix
	ex	af,af'
	dec	a
	push	af
	ex	af,af'
	pop	af
	jr	nz,---_
	pop	hl
	pop	af
	push	af
	ex	af,af'
	ld	de,$0C
	add	hl,de
	djnz	----_
	pop	af
	ret
;-----> Draw a picture
;Input:	ix->sprite
;	a=x
;	l=y
;	b=height	(in pixels)
;	c=width		(in bytes, e.g. 2 would be 16 pixels)
;Output: nothing
; All registers are destroyed except bc', de', hl'
LargeSpriteAND:
	ex	af,af'
	ld	a,c
	push	af
	ex	af,af'
	ld	e,l
	ld	h,$00
	ld	d,h
	add	hl,de
	add	hl,de
	add	hl,hl
	add	hl,hl
	ld	e,a
	and	$07
	ld	c,a
	srl	e
	srl	e
	srl	e
	add	hl,de
	ld	de,LCDBuffer
	add	hl,de
_	push	hl	;largeSpriteLoop1
_	ld	d,(ix)	;largeSpriteLoop2
	ld	e,$FF
	ld	a,c
	or	a
	jr	z,++_
	scf
_	rr	d	;largeSpriteLoop3
	rr	e
	dec	a
	jr	nz,-_
_	ld	a,(hl)	;largeSpriteSkip1
	and	d
	ld	(hl),a
	inc	hl
	ld	a,(hl)
	and	e
	ld	(hl),a
	inc	ix
	ex	af,af'
	dec	a
	push	af
	ex	af,af'
	pop	af
	jr	nz,---_
	pop	hl
	pop	af
	push	af
	ex	af,af'
	ld	de,$0C
	add	hl,de
	djnz	----_
	pop	af
	ret
;-----> Draw a picture
;Input:	ix->sprite
;	a=x
;	l=y
;	b=height	(in pixels)
;	c=width		(in bytes, e.g. 2 would be 16 pixels)
;Output: nothing
; All registers are destroyed except bc', de', hl'
LargeSpriteOR:
	ex	af,af'
	ld	a,c
	push	af
	ex	af,af'
	ld	e,l
	ld	h,$00
	ld	d,h
	add	hl,de
	add	hl,de
	add	hl,hl
	add	hl,hl
	ld	e,a
	and	$07
	ld	c,a
	srl	e
	srl	e
	srl	e
	add	hl,de
	ld	de,LCDBuffer
	add	hl,de
_	push	hl	;largeSpriteLoop1
_	ld	d,(ix)	;largeSpriteLoop2
	ld	e,$00
	ld	a,c
	or	a
	jr	z,++_
_	srl	d	;largeSpriteLoop3
	rr	e
	dec	a
	jr	nz,-_
_	ld	a,(hl)	;largeSpriteSkip1
	or	d
	ld	(hl),a
	inc	hl
	ld	a,(hl)
	or	e
	ld	(hl),a
	inc	ix
	ex	af,af'
	dec	a
	push	af
	ex	af,af'
	pop	af
	jr	nz,---_
	pop	hl
	pop	af
	push	af
	ex	af,af'
	ld	de,$0C
	add	hl,de
	djnz	----_
	pop	af
	ret
;-----> DrawCustomRectangle
;input:	h=x1 l=y2 d=x2 e=y2 a=color
;output:none
DrawCustomRectangle:
api_draw_square:
  ld (api_line_color),a
api_draw_square_noset:
  ld a,h
  cp 96
  jr c,api_drawblock_skip1
  ld h,96
api_drawblock_skip1:
  ld a,d
  cp 96
  jr c,api_drawblock_skip2
  ld d,96
api_drawblock_skip2:
  ld a,l
  cp 64
  jr c,api_drawblock_skip3
  ld l,64
api_drawblock_skip3:
  ld a,e
  cp 64
  jr c,api_drawblock_loop
  ld e,64
api_drawblock_loop:
  ld a,l
  cp e
  ret nc
  push hl
  push de
  ld e,l
  call api_drawline_dontset
  pop de
  pop hl
  inc l
  ld a,(api_line_color)
  cp %10101010
  jr z,api_drawblock_dither
  cp %01010101
  jr nz,api_drawblock_loop
api_drawblock_dither:
  cpl
  ld (api_line_color),a
  jp api_drawblock_loop
api_drawline_set:
  ld (api_line_color),a
api_drawline_dontset:
  push hl
  ld a,h
  call api_drawline_getmask
  ld b,a
  ld a,d
  call api_drawline_getmask
  cpl
  ld c,a
  pop hl
  push bc
  call api_drawline_getbyte
  push hl
  ld h,d
  ld l,e
  call api_drawline_getbyte
  pop de
  push de
  push hl
  sbc hl,de
  jp z,api_drawline_onebyte
  ld a,l
  cp 1
  jp z,api_drawline_skip_inner
  ld b,l
  dec b
  ex de,hl
  inc hl
  ld a,(api_line_color)
api_drawline_loop_inner:
  ld (hl),a
  inc hl
  djnz api_drawline_loop_inner
api_drawline_skip_inner:
  pop de
  pop hl
  pop bc
  call api_drawline_modify
  ex de,hl
  ld b,c
  jp api_drawline_modify
api_drawline_onebyte:
  pop de
  pop hl
  pop bc
  ld a,b
  and c
  ld b,a
api_drawline_modify:
  push bc
  ld a,(api_line_color)
  push bc
  ld c,a
  ld a,b
  and c
  pop bc
  push af
  ld a,b
  cpl
  and (hl)
  ld b,a
  pop af
  or b
  ld (hl),a
  pop bc
  ret
api_drawline_getbyte:
  ld a,h
  ld h,0
  ld b,h
  ld c,l
  add hl,hl
  add hl,bc
  add hl,hl
  add hl,hl
  ld c,a
  srl c
  srl c
  srl c
  add hl,bc
  ld bc,LCDBuffer
  add hl,bc
  ret
api_drawline_getmask:
  and 7
  ld hl,api_drawline_lut
  add a,l
  ld l,a
  ld a,(hl)
  ret
api_drawline_lut:
  .db %11111111
  .db %01111111
  .db %00111111
  .db %00011111
  .db %00001111
  .db %00000111
  .db %00000011
  .db %00000001
